package JavaEE.NetWork.TCP;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// TCP的服务器.
public class EchoServer {
    private ServerSocket serverSocket = null;
    public EchoServer(int Port) throws IOException {
        // 服务器启动,就会绑定到 Port 端口上面.
        serverSocket = new ServerSocket(Port);
    }
    public void start() throws IOException{
        System.out.println("服务器启动!");
        ExecutorService threadPool = Executors.newCachedThreadPool();
        while(true) {
            // 使用accept开始监听serverSocket
            // 当有客户端连接时,返回一个"与客户端建立连接的Socket对象"
            // (没有客户端连接时,阻塞)

            // **- 问题二:每循环一次,就会触发一次"打开文件"操作(构造Socket对象) -**
            // 而一个服务器可能要处理很多个客户端,就可能造成"文件资源泄露"
            // 解决方案客户端退出后,记得关闭文件
            Socket socket = serverSocket.accept();
            // 多线程解决"问题三"
            // 做法1:一个客户端分配一个thread
            // (缺点:客户端过多时,容易造成频繁线程创建与销毁)
            // Thread thread = new Thread(() -> {
            //     try {
            //         // 从lambda表达式外层作用域捕获变量
            //         this.processConnection(socket);
            //     }catch (IOException e){
            //         e.printStackTrace();
            //     }
            // });
            // 做法2:采用线程池
            // (缺点:客户端过多时,可能短时间内出现大量线程,而对于操作系统,线程的数目并不无限)
            // 这里不要使用 newFixedThreadPool(),这会限制客户端的并发数目
            threadPool.submit(() -> {
                try {
                    processConnection(socket);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
            // thread.start();
        }
    }
    // 处理一个客户端/一个链接
    private void processConnection(Socket socket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线!\n",
                socket.getInetAddress(),socket.getPort());
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()){
            // 创建一个Scanner类对象,代表"服务器的输入流" -> "客户端的输出流"
            Scanner scanner = new Scanner(inputStream);
            // 创建一个PrintWriter类对象,代表"服务器的输出流" -> "客户端的输入流"
            PrintWriter writer = new PrintWriter(outputStream);
            // while循环多轮接收请求
            // **- 问题三:这个while循环只有在客户端退出时才能结束,而第一个客户端不退出,第二个客户端就无法进行 -**
            // 那么如何做到多个客户端互不干扰的进行while循环呢? -> 线程~
            while(true){
                // 1. 读取客户端的请求
                // 如果没有接收到客户端请求,则代表这一次处理结束
                if(!scanner.hasNext()){
                    break;
                }
                String request = scanner.next();
                // 2. 通过请求构造响应
                String response = process(request);
                // 3. 将响应写回客户端
                writer.println(response);
                writer.flush();

                System.out.printf("[%s:%d]: req:%s res:%s\n",
                        socket.getInetAddress(),socket.getPort(),request,response);
            }
        }catch(IOException e){
            e.printStackTrace();
        } finally {
            socket.close();
        }
        System.out.printf("[%s:%d] 客户端下线!\n",
                socket.getInetAddress(),socket.getPort());
    }
    private String process(String request){
        return request;
    }

    public static void main(String[] args) throws IOException{
        EchoServer server = new EchoServer(9090);
        server.start();
    }
}


